
#include <xbi/xbiivh.h>

#if 0
#pragma mark Definitions
#endif

typedef enum {
    kXBIVideoOldStyle = 0,
    kXBIVideoNewStyle = 1
} XBIVideoStyle;

#define       _XBINumberOfKnowVideoSize 2
static size_t _XBIKnowFirstHalfSize[_XBINumberOfKnowVideoSize] = { 2704*kXBIDVDSectorSize, 2160*kXBIDVDSectorSize }; // Ordered by style
static size_t _XBIKnowSecondHalfSize[_XBINumberOfKnowVideoSize] = {  815*kXBIDVDSectorSize,  564*kXBIDVDSectorSize }; // Ordered by style

typedef enum {
    kXBIVideoFirstHalf,
    kXBIVideoSecondHalf
} XBIVideoHalfIndex;

struct _XBIIntroVideoHalf {
    byte_t * data;
    size_t size;
    
    XBIVideoHalfIndex  halfIndex;   
    XBIVideoStyle      style;
};

#if 0
#pragma mark Creation - Copy - Deletion
#endif

XBIIntroVideoHalf * XBIIVHCreateFromBuffer(byte_t * buffer, size_t size, XBIError * error) {
    XBIIntroVideoHalf * newIVH = NULL;
    
    int currentStyle = 0;
    
    if (size % kXBIDVDSectorSize != 0) {
        XBILog("Video buffer doesn't have the right size\n");
        *error = kXBIFileWrongFormat;
        goto error;
    }

    newIVH = malloc(sizeof(XBIIntroVideoHalf));    
    if (newIVH == NULL) {
        *error = kXBINotEnoughMemory;
        goto error;
    }
    memset(newIVH, 0, sizeof(XBIIntroVideoHalf));
        
    for (currentStyle=0; currentStyle<_XBINumberOfKnowVideoSize; currentStyle++) {
        if (size == _XBIKnowFirstHalfSize[currentStyle]) {
            newIVH->size = _XBIKnowFirstHalfSize[currentStyle];
            newIVH->halfIndex = kXBIVideoFirstHalf;
            newIVH->style = currentStyle;
            break;
        }
        if (size == _XBIKnowSecondHalfSize[currentStyle]) {
            newIVH->size = _XBIKnowSecondHalfSize[currentStyle];
            newIVH->halfIndex = kXBIVideoSecondHalf;
            newIVH->style = currentStyle;
            break;
        }
    }
    
    if (newIVH->size == 0) {
        XBILog("Video buffer doesn't have the right size\n");
        *error = kXBIFileWrongFormat;
        goto error;
    }
    
    newIVH->data = malloc(newIVH->size);
    if (newIVH->data == NULL) {
        *error = kXBINotEnoughMemory;
        goto error;
    }
    memcpy(newIVH->data, buffer, newIVH->size);
    
    return newIVH;
error:
    XBIIVHDelete(newIVH);    
    return NULL;
}

int  XBILoadVideoFromFile(FILE * file, XBIIntroVideoHalf * * firstIVH, XBIIntroVideoHalf * * secondIVH, XBIError * error) {
    int currentStyle = 0;
    byte_t * fileBuffer = NULL;
    size_t fileSize = XBIGetFileSize(file);
        
    if (fileSize % kXBIDVDSectorSize != 0) {
        XBILog("Video file doesn't have the right size\n");
        *error = kXBIFileWrongFormat;
        goto error;
    }
    
    for (currentStyle=0; currentStyle<_XBINumberOfKnowVideoSize; currentStyle++) {
        if (fileSize == (_XBIKnowFirstHalfSize[currentStyle] + _XBIKnowSecondHalfSize[currentStyle])) {
            break;
        }
    }
    
    if (currentStyle == _XBINumberOfKnowVideoSize) { // Size isn't a known full video size
        XBILog("Error : Unknown size of ISO file\n");
        goto error;
    }
    
    fileBuffer = XBICreateBufferFromFile(file, &fileSize);
    
    if (fileBuffer == NULL) {
        *error = kXBINotEnoughMemory;
        goto error;
    }
    
    *firstIVH  = XBIIVHCreateFromBuffer(fileBuffer, _XBIKnowFirstHalfSize[currentStyle], error);
    *secondIVH = XBIIVHCreateFromBuffer(fileBuffer + _XBIKnowFirstHalfSize[currentStyle], _XBIKnowSecondHalfSize[currentStyle], error);
    
    if (fileBuffer != NULL) {
        free(fileBuffer);
        fileBuffer = NULL;
    }

    return 0;    
error:
    if (fileBuffer != NULL) {
        free(fileBuffer);
        fileBuffer = NULL;
    }
    return -1;
}

XBIIntroVideoHalf * XBIIVHCopy(XBIIntroVideoHalf * ivh) {
    XBIIntroVideoHalf * ivhCopy = NULL;
    
    if (ivh == NULL)
        goto error;
    
    ivhCopy = malloc(sizeof(XBIIntroVideoHalf));    
    if (ivhCopy == NULL)
        goto error;
    memset(ivhCopy, 0, sizeof(XBIIntroVideoHalf));

    if (ivh->data != NULL) {
        ivhCopy->data = malloc(ivh->size);
        if (ivhCopy->data == NULL)
            goto error;
        memcpy(ivhCopy->data, ivh->data, ivh->size);
    }
        
    ivhCopy->size = ivh->size;

    ivhCopy->halfIndex = ivh->halfIndex;
    ivhCopy->style = ivh->style;
    
    return ivhCopy;
    
error:
    XBIIVHDelete(ivhCopy);
    return NULL;
}

void XBIIVHDelete(XBIIntroVideoHalf * ivh) {
    if (ivh != NULL) {
        if (ivh->data != NULL) {
            free(ivh->data);
            ivh->data = NULL;
        }
        free(ivh);
    }
}

#if 0
#pragma mark Data manipulation
#endif

uint32_t XBIIVHGetCRC32(XBIIntroVideoHalf * ivh) {
    uint32_t crc32 = 0;
    if (ivh == NULL || ivh->data == NULL || ivh->size == 0)
        return 0;
    crc32 = XBIComputeCRC32(ivh->data, ivh->size);
    if (ivh->style == kXBIVideoOldStyle) {
        if (ivh->halfIndex == kXBIVideoFirstHalf) {
            if (crc32 != 0xC506923B) {
                XBILog("Video is corrupted\n");
            } else {
                XBILog("Video is sane\n");
            }
        } else if (ivh->halfIndex == kXBIVideoSecondHalf) {
            if (crc32 != 0x1F51DA34) {
                XBILog("Video is corrupted\n");
            } else {
                XBILog("Video is sane\n");
            }
        }
    } else if (ivh->style == kXBIVideoNewStyle) {
        if (ivh->halfIndex == kXBIVideoFirstHalf) {
            if (crc32 != 0x81DF6964) {
                XBILog("Video is corrupted\n");
            } else {
                XBILog("Video is sane\n");
            }
        } else if (ivh->halfIndex == kXBIVideoSecondHalf) {
            if (crc32 != 0xE47F6A94) {
                XBILog("Video is corrupted\n");
            } else {
                XBILog("Video is sane\n");
            }
        }
    }
        
    return crc32;
}

#if 0
#pragma mark Accessors
#endif

size_t XBIIVHGetSize(XBIIntroVideoHalf * ivh) {
    if (ivh != NULL)
        return ivh->size;
    return -1;
}

byte_t * XBIIVHGetData(XBIIntroVideoHalf * ivh) {
    if (ivh != NULL)
        return ivh->data;
    return NULL;    
}
